//======================================================================
//    We will gone,the word kept   (license)
//======================================================================
//    version & features log
//======================================================================
/******************************************************************************
 This Source Code Form is subject to the terms of the
 Open Hardware Description License, v. 1.0. If a copy
 of the OHDL was not distributed with this file, You
 can obtain one at http://juliusbaxter.net/ohdl/ohdl.txt

 Description: Store buffer
 Currently a simple single clock FIFO, but with the ambition to
 have combining and reordering capabilities in the future.

 Copyright (C) 2013 Stefan Kristiansson <stefan.kristiansson@saunalahti.fi>

 ******************************************************************************/

module sfifo_task  #(
      parameter ADDR_WIDTH = 4, // pointer_width ,depth_width is 2**dw ,
      //parameter DATA_WIDTH  = 16 ,
      parameter PROG_EMPTY_OFST = 1 ,  // : ASSERT VALUE .
      parameter PROG_FULL_OFST  = 12,   // : ASSERT VALUE .
      parameter FWFT_EN = "FALSE",
      parameter OUTREG_EN = "FALSE"
    ) (
    input         clk,
    input         rst,

    input             wr_en_i,

    input             rd_en_i,

    output             prog_empty ,
    output             prog_full  ,
    output   [ADDR_WIDTH:0]    data_count,
    output             full_o ,
    output             empty_o ,

    output   val_wr ,
    output   val_rd ,
    output   val_regceb ,
    output   [ADDR_WIDTH-1:0] rdaddr ,
    output   [ADDR_WIDTH-1:0] wraddr

    );
    // localparam   MIN_DEPTH   = 4;

    localparam AEMPTY_OFST  = (PROG_EMPTY_OFST<3 ) ? 3 :
                                ( (PROG_EMPTY_OFST >= 2**ADDR_WIDTH) ? (2**ADDR_WIDTH - 1'b1) : PROG_EMPTY_OFST ) ;

    localparam AFULL_OFST  = (PROG_FULL_OFST<4 ) ? 4 :
                                ( (PROG_FULL_OFST >= 2**ADDR_WIDTH) ? (2**ADDR_WIDTH - 1'b1) : PROG_FULL_OFST ) ;

    reg [ADDR_WIDTH:0] write_pointer;
    reg [ADDR_WIDTH:0] read_pointer;
    reg [ADDR_WIDTH:0] read_pointer_reg ;

    reg               empty_r_1d ;
    reg               fifo_valid ;


//
  reg    wr_en_i_r  ;
  always@(posedge clk )
      begin
          wr_en_i_r <= wr_en_i ;
      end

  reg empty_r ;
  reg full_r   ;


  reg    [ADDR_WIDTH:0]usedw ;
  assign data_count = usedw;
// //   wire   empty = (~|usedw) ; //  usedw == 0;
// //   wire   full  = usedw[ADDR_WIDTH] ; //! After the FIFO is full, usedw is not affected by the wr_en_i signal .

  assign   val_rd = (FWFT_EN == "FALSE") ? (!empty_r && rd_en_i) : 1'b1 ;
  assign   val_wr = (!full_r && wr_en_i ) ;

/////////////////////////////////////////////////////////////////////////////////////
//function: :usedw;

    always @(posedge clk or posedge rst)begin
        if(rst)begin
            usedw <= 0;
        end
        else begin
            case ({val_rd,val_wr})
                2'b01:begin
                    usedw <= usedw + 1'b1 ;
                end
                2'b10:begin
                    usedw <= usedw - 1'b1 ;
                end
                default : begin
                    usedw <= usedw ;
                end
            endcase
        end
    end


    reg prog_full_r  = 1'b0 ;
    reg prog_empty_r = 1'b1 ;

    assign prog_empty = prog_empty_r ;
    assign prog_full  = prog_full_r  ;

    assign full_o  = full_r  ;
    assign empty_o =  (OUTREG_EN=="FALSE") ? empty_r : !fifo_valid ;//in outreg,empty is delayed(&???)

   always@(posedge clk  or posedge rst)
      begin
          if(rst)
              begin
                  empty_r <= 1'b1 ;
                  full_r  <= 1'b0 ;
                  //
                  prog_empty_r <= 1'b1 ;
                  prog_full_r  <= 1'b0 ;
              end
          else
              begin
                case ({val_rd,val_wr} )
                    2'b01 :begin  //( val_wr && !val_rd )
                        //
                        empty_r <= 1'b0 ;
                        if (&usedw[ADDR_WIDTH-1:0]) begin
                            full_r <= 1'b1 ;
                        end
                        //
                        if (  usedw[ADDR_WIDTH-1:0] == AEMPTY_OFST ) begin //! usedw 每次变化为1 ，故可以取等号降低资源。
                            prog_empty_r <= 1'b0 ;
                        end
                        if ( usedw[ADDR_WIDTH-1:0] ==( AFULL_OFST - 1'b1 ) ) begin
                            prog_full_r <= 1'b1 ;
                        end
                    end
                    2'b10 :begin  // (val_rd && !val_wr)
                        //
                        full_r <= 1'b0 ;
                        if ((~|usedw[ADDR_WIDTH-1:1]) && usedw[0]) begin
                            empty_r <= 1'b1 ;
                        end
                        //
                        if ( usedw[ADDR_WIDTH-1:0] == (AEMPTY_OFST + 1'b1) ) begin
                            prog_empty_r <= 1'b1 ;
                        end
                        if (usedw[ADDR_WIDTH-1:0] == AFULL_OFST ) begin
                            prog_full_r <= 1'b0 ;
                        end
                    end
                    default : begin
                        empty_r <= empty_r ;
                        full_r  <= full_r  ;
                        //
                        prog_empty_r <= prog_empty_r ;
                        prog_full_r  <= prog_full_r  ;
                    end
                endcase
              end
      end
//write pointer
    always @(posedge clk  or posedge rst) begin
        if (rst) begin
          write_pointer <= 0;
        end
        else
            begin
              if (val_wr)  //(!full && wr_en_i)
                  write_pointer <= write_pointer + 1'd1;
            end
    end
assign wraddr = write_pointer[ADDR_WIDTH-1:0];
//read_pointer,no sure about full&empty
generate
    if (FWFT_EN == "FALSE") begin : g_std
    //read_pointer is registered; updated in next val_rd
        always @(posedge clk  or posedge rst) begin
            if (rst) begin
              read_pointer  <= 0;
            end else begin if (val_rd)  //(!empty && rd_en_i)
                        read_pointer <= read_pointer + 1'd1;
            end
        end
    end else begin :g_fwft
    //read_pointer is comp; updated in same of  val_rd
        always @(*) begin
            if (val_rd)  //(!empty && rd_en_i)
                    read_pointer <= read_pointer_reg + 1'd1;//combine logic
            else
                    read_pointer <= read_pointer_reg ;
        end
        always @(posedge clk  or posedge rst) begin
            if (rst) begin
              read_pointer_reg  <= 0;
            end
            else
                begin
                    read_pointer_reg <= read_pointer ;
                end
        end
    end
endgenerate
    assign rdaddr = read_pointer [ADDR_WIDTH-1:0];
////----------------------------------------------------
//OUTREG_EN
//------------------------------------------------------
//assign fifo_rd_en_o = (((!empty_r_1d) & !fifo_valid) | rd_en_i) & !fifo_empty_i ;
//  assign empty_o = !fifo_valid;
assign val_regceb = (OUTREG_EN=="FALSE") ? 1'b1 :  (((!empty_r_1d) & !fifo_valid) | rd_en_i) & !empty_r ;
always @(posedge clk  or posedge rst) begin
            if (rst) begin
              fifo_valid  <= 0;
              empty_r_1d <= 0 ;
            end
            else begin 
                empty_r_1d = empty_r ;
                if (val_regceb) //be carefull，wrong rd—en—i make the fifo die
                    fifo_valid <= 1;
                else if (rd_en_i & empty_r)
                    fifo_valid <= 0;
            end
        end
            

endmodule
